//////////////////////////////////////////////////////////////////////////
//
// Vector Graph Core2D
//
// Module:
//        ElementTypes2D.cs
//
// Description:
//        Implementation of basic elements definition
//
// Author:
//        Created on 2008.04.14 by Destrayer
//
//////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Text;

namespace VectorGraph.Core2D
{
    public class Element2D
    {
        public static readonly double Precition = 1e-3;

        public static double AbsTol(double radius, double relativeTolerance)
        {
            return radius * relativeTolerance;
        }
    }

    public struct Point2i
    {
        public int X;
        public int Y;

        public Point2i(int x, int y)
        {
            this.X = x;
            this.Y = y;
        }

        public static Point2i operator +(Point2i lhs, Point2i rhs)
        {
            Point2i ret;
            ret.X = lhs.X + rhs.X;
            ret.Y = lhs.Y + rhs.Y;
            return ret;
        }

        public static Point2i operator -(Point2i lhs, Point2i rhs)
        {
            Point2i ret;
            ret.X = lhs.X - rhs.X;
            ret.Y = lhs.Y - rhs.Y;
            return ret;
        }

        public int CrossProduct(Point2i pt)
        {
            return this.X * pt.Y - this.Y * pt.X;
        }

        public int DotProduct(Point2i pt)
        {
            return this.X * pt.X + this.Y * pt.Y;
        }
    }

    public struct Point2d
    {
        public double X;
        public double Y;

        public Point2d(double x, double y)
        {
            this.X = x;
            this.Y = y;
        }

        public static Point2d operator +(Point2d lhs, Point2d rhs)
        {
            Point2d ret;
            ret.X = lhs.X + rhs.X;
            ret.Y = lhs.Y + rhs.Y;
            return ret;
        }

        public static Point2d operator -(Point2d lhs, Point2d rhs)
        {
            Point2d ret;
            ret.X = lhs.X - rhs.X;
            ret.Y = lhs.Y - rhs.Y;
            return ret;
        }

        public double Length
        {
            get { return Math.Sqrt(this.X * this.X + this.Y * this.Y); }
        }

        public double SqrLength
        {
            get { return this.X * this.X + this.Y * this.Y; }
        }

        public double Angle
        {
            get { return (Math.Atan2(this.Y, this.X)); }
        }

        public void Scale(double rate)
        {
            this.X *= rate;
            this.Y *= rate;
        }

        public void Inverse()
        {
            this.X = -this.X;
            this.Y = -this.Y;
        }

        public bool Equals(Point2d pt, double tolerance)
        {
            return Math.Abs(pt.X - this.X) < tolerance && Math.Abs(pt.Y - this.Y) < tolerance;
        }

        public double CrossProduct(Point2d pt)
        {
            return this.X * pt.Y - this.Y * pt.X;
        }

        public double DotProduct(Point2d pt)
        {
            return this.X * pt.X + this.Y * pt.Y;
        }

        public double Distance(Point2d pt)
        {
            return Math.Sqrt((pt.X - this.X) * (pt.X - this.X) + (pt.Y - this.Y) * (pt.Y - this.Y));
        }

        public double SqrDistance(Point2d pt)
        {
            return (pt.X - this.X) * (pt.X - this.X) + (pt.Y - this.Y) * (pt.Y - this.Y);
        }

        public double SweepAngle(Point2d pt)
        {
            double angle = Math.Atan2(this.CrossProduct(pt), this.DotProduct(pt));
            if (angle < 0)
            {
                angle += 2 * Math.PI;
            }
            return angle;
        }
    }

    public struct Rectangle2i
    {
        public Point2i LeftBottom;
        public Point2i RightTop;

        #region properties
        public int Left
        {
            get { return this.LeftBottom.X; }
            set { this.LeftBottom.X = value; }
        }

        public int Bottom
        {
            get { return this.LeftBottom.Y; }
            set { this.LeftBottom.Y = value; }
        }

        public int Right
        {
            get { return this.RightTop.X; }
            set { this.RightTop.X = value; }
        }

        public int Top
        {
            get { return this.RightTop.Y; }
            set { this.RightTop.Y = value; }
        }
        #endregion

        public Rectangle2i(Point2i leftBottom, Point2i rightTop)
        {
            this.LeftBottom = leftBottom;
            this.RightTop = rightTop;
        }

        public Rectangle2i(int left, int bottom, int right, int top)
        {
            this.LeftBottom.X = left;
            this.LeftBottom.Y = bottom;
            this.RightTop.X = right;
            this.RightTop.Y = top;
        }
    }

    public struct Rectangle2d
    {
        public Point2d LeftBottom;
        public Point2d RightTop;

        #region properties
        public double Left
        {
            get { return this.LeftBottom.X; }
            set { this.LeftBottom.X = value; }
        }

        public double Bottom
        {
            get { return this.LeftBottom.Y; }
            set { this.LeftBottom.Y = value; }
        }

        public double Right
        {
            get { return this.RightTop.X; }
            set { this.RightTop.X = value; }
        }

        public double Top
        {
            get { return this.RightTop.Y; }
            set { this.RightTop.Y = value; }
        }
        #endregion

        public Rectangle2d(Point2d pt)
        {
            this.LeftBottom = pt;
            this.RightTop = pt;
        }

        public Rectangle2d(Point2d leftBottom, Point2d rightTop)
        {
            this.LeftBottom = leftBottom;
            this.RightTop = rightTop;
        }

        public Rectangle2d(double left, double bottom, double right, double top)
        {
            this.LeftBottom.X = left;
            this.LeftBottom.Y = bottom;
            this.RightTop.X = right;
            this.RightTop.Y = top;
        }
    }

    public struct Beeline2d
    {
        public Point2d BasePt;
        public Point2d Direction;

        public Beeline2d(Point2d basePt, Point2d direction)
        {
            this.BasePt = basePt;
            this.Direction = direction;
        }

        public Beeline2d(double x1, double y1, double x2, double y2)
        {
            this.BasePt.X = x1;
            this.BasePt.Y = y1;
            this.Direction.X = x2 - x1;
            this.Direction.Y = y2 - y1;
        }
    }

    public struct Circle2d
    {
        public Point2d CenterPt;
        public double Radius;

        public Circle2d(Point2d centerPt, double radius)
        {
            this.CenterPt = centerPt;
            this.Radius = radius;
        }

        public Circle2d(double x, double y, double radius)
        {
            this.CenterPt.X = x;
            this.CenterPt.Y = y;
            this.Radius = radius;
        }
    }

    public struct Segment2d
    {
        public Point2d StartPt;
        public Point2d EndPt;

        public Segment2d(Point2d startPt, Point2d endPt)
        {
            this.StartPt = startPt;
            this.EndPt = endPt;
        }

        public Segment2d(double x1, double y1, double x2, double y2)
        {
            this.StartPt.X = x1;
            this.StartPt.Y = y1;
            this.EndPt.X = x2;
            this.EndPt.Y = y2;
        }

        public Beeline2d ToBeeline()
        {
            return new Beeline2d(this.StartPt, this.EndPt - this.StartPt);
        }
    }

    public struct Arc2d
    {
        public Point2d CenterPt;
        public Point2d StartVector;
        public Point2d EndVector;

        public Arc2d(Point2d centerPt, Point2d startVector, Point2d endVector)
        {
            this.CenterPt = centerPt;
            this.StartVector = startVector;
            this.EndVector = endVector;
        }

        public double Radius
        {
            get { return this.StartVector.Length; }
        }

        public double StartAngle
        {
            get { return this.StartVector.Angle; }
        }

        public double SweepAngle
        {
            get { return this.StartVector.SweepAngle(this.EndVector); }
        }

        public Circle2d ToCircle()
        {
            return new Circle2d(this.CenterPt, this.StartVector.Length);
        }
    }

    public class Path2d
    {
        protected List<Point2d> vertices;

        public Path2d()
        {
            this.vertices = new List<Point2d>();
        }

        public int Count
        {
            get { return this.vertices.Count; }
        }

        public virtual int EdgeCount
        {
            get { return this.Vertices.Count - 1; }
        }

        public ICollection<Point2d> Vertices
        {
            get { return this.vertices; }
        }

        public virtual ICollection<Segment2d> Edges
        {
            get
            {
                List<Segment2d> ret = new List<Segment2d>();
                for (int i = 0; i < this.vertices.Count - 1; i++)
                {
                    ret.Add(new Segment2d(this.vertices[i], this.vertices[i + 1]));
                }
                return ret;
            }
        }

        public void AddVertex(Point2d vertex)
        {
            this.vertices.Add(vertex);
        }

        public void AddVertex(double x, double y)
        {
            this.vertices.Add(new Point2d(x, y));
        }

        public Point2d Vertex(int index)
        {
            System.Diagnostics.Debug.Assert(index >= 0 && index < this.Count);
            return this.vertices[index];
        }

        public void SetVertex(int index, Point2d pt)
        {
            System.Diagnostics.Debug.Assert(index >= 0 && index < this.Count);
            this.vertices[index] = pt;
        }

        public virtual Segment2d Edge(int index)
        {
            System.Diagnostics.Debug.Assert(index >= 0 && index < this.Count - 1);
            return new Segment2d(this.vertices[index], this.vertices[index + 1]);
        }
    }

    public class Polygon2d : Path2d
    {
        public Polygon2d()
            : base()
        {

        }

        public override int EdgeCount
        {
            get { return this.Vertices.Count; }
        }

        public override ICollection<Segment2d> Edges
        {
            get
            {
                List<Segment2d> ret = new List<Segment2d>();
                for (int i = 0; i < this.vertices.Count - 1; i++)
                {
                    ret.Add(new Segment2d(this.vertices[i], this.vertices[i + 1]));
                }
                ret.Add(new Segment2d(this.vertices[this.Count - 1], this.vertices[0]));
                return ret;
            }
        }

        public override Segment2d Edge(int index)
        {
            System.Diagnostics.Debug.Assert(index >= 0 && index < this.Count);
            return new Segment2d(this.vertices[index], this.vertices[index + 1]);
        }
    }
}
